﻿namespace CloudNinja.Metering.Tests.Provider
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.IO;
    using System.Linq;
    using System.Text;

    using CloudNinja.Metering.Common;
    using CloudNinja.Metering.Common.Logging;
    using CloudNinja.Metering.Data.Models;
    using CloudNinja.Metering.Providers.AzureBlob;
    using CloudNinja.Metering.Tests.Support;

    using Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling;
    using Microsoft.WindowsAzure.Storage;
    using Microsoft.WindowsAzure.Storage.Blob;

    using Moq;

    using Xunit;

    public class BlobSizeProviderTests
    {
        [Fact]
        public void CanGetBLobSizes()
        {
            const string ConnectionString = GlobalTestSettings.StorageConnectionString;
            var storageAccounts = new Dictionary<string, TenantsStorageAccount>
                {
                    {
                        "tenant1",
                        new TenantsStorageAccount
                            {
                                ConnectionString = ConnectionString,
                                TenantIdPattern = "tnt[tpn]-{.+}",
                                TenantBaseContainerPath = string.Empty
                            }
                        }
                };

            var loggerMock = new Mock<ILogger>();
            loggerMock.Setup(l => l.LogException(It.IsAny<Exception>()));
            loggerMock.Setup(l => l.LogError(It.IsAny<string>()));
            loggerMock.Setup(l => l.LogInformation(It.IsAny<string>()));

            var repository = new List<MeterData>();

            var meteringRepository = new MeteringRepositoryMock(repository);

             var meteringContextMock = new Mock<IMeteringContext>();
            
            var retryStrategy = new Incremental();
            var retryPolicy = new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);


            meteringContextMock.Setup(mc => mc.AzureStorageRetryPolicy).Returns(retryPolicy);

            const string ContainerName = "tntt-1";
            const string BlobName = "testblob";

            this.RemoveBlob(ContainerName, BlobName, GlobalTestSettings.StorageConnectionString);
            this.PutBlock(ContainerName, BlobName, GlobalTestSettings.StorageConnectionString, 1024 * 1024);

            var blobSizeProvider = new BlobSizeProvider(
            TimeSpan.FromMinutes(1), storageAccounts, loggerMock.Object, meteringRepository, meteringContextMock.Object);

            blobSizeProvider.Collect();

            Assert.True(repository.Count > 0);
            Assert.Equal(1024, repository[0].Value);

            this.RemoveBlob(ContainerName, BlobName, GlobalTestSettings.StorageConnectionString);
        }

        [Fact]
        public void CanGetCorrectBLobSize()
        {
            const string ContainerName = "testcontainer";
            const string BlobName = "testblob";

            this.RemoveBlob(ContainerName, BlobName, GlobalTestSettings.StorageConnectionString);
            this.PutBlock(ContainerName, BlobName, GlobalTestSettings.StorageConnectionString, 1024 * 1024);

            var storageAccount = CloudStorageAccount.Parse(GlobalTestSettings.StorageConnectionString);
            var client = storageAccount.CreateCloudBlobClient();
            var container = client.GetContainerReference(ContainerName);
            var context = new OperationContext();
            var blobs = container.ListBlobs(null, true, BlobListingDetails.Metadata, null, context).ToList();
            var myBlobs = blobs.OfType<CloudBlockBlob>().ToList();

            Assert.Equal(1, blobs.Count);
            Assert.Equal(1, myBlobs.Count);

            Assert.Equal(1024 * 1024, myBlobs[0].Properties.Length);

            this.RemoveBlob(ContainerName, BlobName, GlobalTestSettings.StorageConnectionString);
            blobs = container.ListBlobs(null, true, BlobListingDetails.Metadata, null, context).ToList();

            Assert.Equal(0, blobs.Count);
        }

        private void PutBlock(string containerName, string name, string connectionString, int size)
        {
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            var client = storageAccount.CreateCloudBlobClient();
            var container = client.GetContainerReference(containerName);
            container.CreateIfNotExists();
            var blockBlob = container.GetBlockBlobReference(name);
            var builder = new StringBuilder(size);
            for (var i = 0; i < size; i++)
            {
                builder.Append("E");
            }

            var asciiEncoding = new ASCIIEncoding();
            var buffer = asciiEncoding.GetBytes(builder.ToString());

            using (var stream = new MemoryStream(buffer))
            {
                blockBlob.UploadFromStream(stream);
            }
        }

        private void RemoveBlob(string containerName, string name, string connectionString)
        {
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            var client = storageAccount.CreateCloudBlobClient();
            var container = client.GetContainerReference(containerName);
            var blockBlob = container.GetBlockBlobReference(name);
            blockBlob.DeleteIfExists();
        }
    }
}
